﻿#pragma once

#include <DirectXMath.h>
#include <EASTL/vector.h>
#include <game/animation/avatar_id.h>
#include <graphics/transform_def.h>
#include <static_string.h>
#include <cached_file.h>

struct aiNode;

namespace ifire::game {
class Avatar;
class ArmatureTree;

struct ArmatureNode {
  // 此项目所属的True
  ArmatureTree* owner;
  // 此节点的索引
  int index;
  // 对应的AvatarID
  int avatar_id;
  // 对应的骨骼
  int bone_id;
  // 此节点的名称
  ikit::sstr name;

  // 此节点的父节点
  ArmatureNode* parent;
  // 此项目的子节点
  eastl::vector<ArmatureNode*> children;

  // 相对于节点父级的变换。
  IMat transformation;
  // T-Pose的变换矩阵（相对）
  Trans2 tpose_relative;
  // T-Pose的变换矩阵（最终）
  IMat tpose_mat;
  // T-Pose的骨骼长度
  float tpose_length;
  // T-Pose的骨骼总长度（用于定位）
  float tpose_total_length;

  ArmatureNode(ArmatureTree* _owner, int _index, ArmatureNode* parent_node, int child_count,
      ikit::sstr _name, FMat _transformation);
  ~ArmatureNode() = default;

  // 判断该节点是否是RootMotion的Node
  bool IsRootMotionNode() const;
};

/// <summary>
/// 用于管理整个ArmatureNode
/// </summary>
class ArmatureTree {
public:
  // 通过载入的项目进行创建
  ArmatureTree(const aiNode* root_node, bool convert_units);
  // 通过一个XmlNode创建
  ArmatureTree(const ikit::XmlNode& root_node);
  // 通过一个CachedFile创建
  ArmatureTree(ikit::CachedFile& f);

  ArmatureTree(const ArmatureTree& lhs) = delete;
  ArmatureTree& operator=(const ArmatureTree& lhs) = delete;
  ~ArmatureTree();

  friend struct ArmatureNode;

  // 将项目保存到CachedFile
  void Save(ikit::CachedFile& f);

  // 设置RootMotion的Node
  void SetRootMotionNode(ikit::sstr name);
  // 通过名称查找一个Node
  ArmatureNode* Find(ikit::sstr name) const;
  // 打印为XML格式
  void PrintXml(const std::wstring& filename) const;
  // 打印为XML的正式格式（用于Avatar的读取）
  void PrintArmatureXml(const std::wstring& filename) const;
  // 打印各种矩阵的信息
  void PrintData(const char* file_name) const;
  // 通过Avatar来配置项目的ID
  void SetByAvatar(Avatar* avatar);
  // 打印这个节点的结构
  void PrintHierarchy() const;
  // 计算t-pose的变换矩阵
  void CalcPose(Avatar* avatar);

  // ------
  // 属性访问
  // ------
  ArmatureNode* Root() { return root_node_; }
  const ArmatureNode* RootMotion() const { return root_motion_node_; }
  size_t size() const { return nodes_.size(); }
  const eastl::vector<ArmatureNode*>& Nodes() const { return nodes_; }
  IVec GetBoxSize() const { return box_size_; }
  float GetBoxWidth() const { return XMVectorGetX(box_size_); }
  float GetBoxHeight() const { return XMVectorGetY(box_size_); }
  float GetBoxLong() const { return XMVectorGetZ(box_size_); }

private:  
  // 如果是通过XmlNode或者CachedFile创建的，需要手动建立关系
  void BuildRelation();
  // 用于从aiNode中创建一个Node
  ArmatureNode* CreateFrom(ArmatureNode* parent_node, const aiNode* node);
  // 打印这个节点的结构
  void PrintHierarchy(const ArmatureNode* node) const;
  // 计算T-Pose
  void CalcTPoseHierarchy(DirectX::FXMMATRIX parent, ArmatureNode* node,
      Avatar* avatar, float total_length);
  // 计算包围盒
  void CalcBoxSize();

private:
  // 根Node
  ArmatureNode* root_node_;
  // 骨骼动画开始节点
  const ArmatureNode* root_motion_node_;

  // Node的列表
  eastl::vector<ArmatureNode*> nodes_;
  // 是否转换单位
  bool convert_units_;
  // 是否已经设置了Avatar
  bool is_set_avatar;
  // 是否已经计算了Pose
  bool is_calc_pose;
  // 包围盒
  IVec out_min_;
  IVec out_max_;
  // 包围盒大小
  IVec box_size_;
};

} // namespace ifire::game